import java.util.ArrayList;
import java.util.TreeMap;
import java.awt.Polygon;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Line2D.Double;
import java.io.*;

/**
 * 
 * @author Adam and Terry
 *
 */
public class Region {
	TreeMap<String, Road> roads;
	//private String filename;
	private TreeMap<String, Area> city;
	private TreeMap<String, Rail> rail;
	private TreeMap<String, Road> road;
	private TreeMap<String, State> state;
	private TreeMap<String, CustomPoint> customPoint; 
	private ArrayList<CustomPoint> customPoints; 
	private Rectangle2D.Double bounds;
	private boolean changed;
    private double minLat = 999, maxLat = -999;
    private double minLon = 999, maxLon = -999;
    private MapFileManager manager;
    private static int count =0;
    
    private DiGraph roadGraph;
    
	
	/**
	 * Region Constructor 
	 * @param filename
	 */
	
	public Region()
	{
		//this.filename = filename;
		//points = new ArrayList<Point>();
		changed = false;
		city = new TreeMap<String, Area>(); 
		rail = new TreeMap<String, Rail>(); 
		road = new TreeMap<String, Road>(); 
		state = new TreeMap<String, State>(); 
		//point = new TreeMap<String, Point>(); 
	}
	
	public MapFileManager getManager()
	{
		return this.manager;
	}
	
	public void setChanged(boolean changed)
	{
		this.changed = changed;
	}
	
	public boolean getChanged()
	{
		return this.changed;
	}
	
	/**
	 * Clear all the points from the arraylist after save 
	 */
	
	public void clearList()
	{
	//	this.points.removeAll(points);
	}
	
	/**
	 * this method will check to see if any values has been changes. 
	 * This could be the name of a rail or road or the colour of a suburb
	 * Used to check if ok to save 
	 * @return boolean
	 */

	

	public Area findSuburb(String name)
	{	
		for (Area value : city.values()){			
			if(value.getName().equalsIgnoreCase(name)){
				return value;
			}
		}

		return null;
	}
	
	public Area findSuburb(Point2D.Double lonlat, double tolerance)
	{
		for (Area value : city.values()){			
			if(drawPolygon(value).contains(lonlat)){
				return value;
			}
		}
		return null;
	}
	
	public Polygon drawPolygon(Area a)
	{
		Polygon pol = new Polygon();
		
		for(Point2D.Double v : a.getPoints()){
			pol.addPoint((int)v.getX(),(int)v.getY());
		}
		
		return pol;
	}

	public Road findRoad(Point2D.Double lonlat)
	{
		

		
		for (Road value : road.values())
		{
			
			for(Line2D.Double d : value.getSections())
			{
				
				double v = d.ptLineDist(lonlat);
		
				if(v <= 0.5){
					
					return value;
				}
			}
			
		}
		
		return null;
	}
	
	public Rail findRail(Point2D.Double lonlat)
	{
		for (Rail value : rail.values())
		{			
			if(value.getDrawPath().contains(lonlat))
			{
				return value;
			}
		}
		return null;
	}
	
	
	public Rectangle2D.Double getBounds() {
      
		return this.bounds;
    }
	/**
	 * 
	 */
	public int getArrayListSize()
	{
	 return 0;	//return this.points.size();
	}
	
	/**
	 * 
	 * @return the string values from the arraylist and tree map 
	 */
	public String arrayListToString()
	{
		String s = "";
		
		for (CustomPoint p : customPoint.values())
		{
			s = s + p.toString() + "end\n";
		}

		for(CustomPoint p: customPoints) {
			s = s + p.toString() + "end\n";
		}
		return s;
	}
	
	/**
	 * 
	 * @param point
	 */
	/*public void addPointObject(Point point)
	{
		this.points.add(point);
	}*/
	
	/**
	 * 
	 * @return city treemap 
	 */
	public TreeMap<String, Area> getArea()
	{
		return this.city;
	}
	
	/**
	 * 
	 * @return rail treemap 
	 */
	public TreeMap<String, Rail> getRail()
	{
		return this.rail;
	}
	
	
	
	/**
	 * 
	 * @return road treemap 
	 */
	
	public TreeMap<String, Road> getRoad()
	{
		return this.road;
	}
	
	/**
	 * 
	 * @return
	 */
	public DiGraph getRoadGraph() {
		return roadGraph;
	}
	
	/**
	 * 
	 * @return state treemap 
	 */
	
	public TreeMap<String, State> getState()
	{
		return this.state;
	}
	/**
	 * 
	 * @return point treemap 
	 */
	public TreeMap<String, CustomPoint> getPoint()
	{
		return this.customPoint;
	}
	
	/**
	 * load all the files from the scenario.scn file 
	 */
	public void loadFromFile(File filename)
	{
		if(filename != null){
		try{
			  FileInputStream fstream = new FileInputStream(filename.getName());
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));
			  String strLine;
			  
			   manager = new MapFileManager();
			  
			 
			 
			  roadGraph = manager.getRoadGraph();
			  
			  while ((strLine = br.readLine()) != null)   {
				  String[] filetypes = strLine.split(",");
				  
				  if(filetypes[2].contains("|")){
					  filetypes[2] = "\\" +filetypes[2];
				  }
				  
				  if(filetypes.length == 3)
				  {  
					  if(filetypes[0].trim().equalsIgnoreCase("Road"))
					  {
						  road = manager.loadRoads(filetypes[1].trim(), filetypes[2].trim());
					  }
					  else if(filetypes[0].trim().equalsIgnoreCase("Area"))
					  {
						  city = manager.loadSuburbs(filetypes[1].trim(), filetypes[2].trim());
					  }else
					  {
						  readFile(filetypes[0].trim(),filetypes[1].trim(), filetypes[2].trim());
					  }
				  }
				  
			  }
			  in.close();
		   }
		catch(FileNotFoundException ex){
			
			System.out.println("file not found");
		}
		   catch (Exception e)
		   {//Catch exception if any
			  System.err.println("Error: " + e.getMessage());
		   }
		   
		}
		  
	}
	
	/**
	 * 
	 * @param classtype
	 * @param file
	 * @param delimiter
	 * @throws IOException
	 */

	private void readFile(String classtype, String file, String delimiter) throws IOException {
	
			LineNumberReader in = new LineNumberReader(new FileReader(file));
			
			if(in.read() != -1){
			String stateName = in.readLine().trim();
		
			while (!stateName.equalsIgnoreCase("finalend")) {
				
				if(classtype.equalsIgnoreCase("State")){
					State r = new State(stateName, delimiter);
					r.read(in, stateName);
					state.put(r.getName(), r);
					checkLanLog(r);
				}
				else if(classtype.equalsIgnoreCase("Area"))
				{
					
					
					/*Area r = new Area(stateName, delimiter);
					r.read(in, stateName);
					city.put(r.getName(), r);*/
				}
				else if(classtype.equalsIgnoreCase("Road"))
				{
					/*Road r = new Road(stateName, delimiter);
					r.read(in, stateName);
					road.put(r.getName(), r);*/
					
					
					
				}
				else if(classtype.equalsIgnoreCase("Rail")){
					
			
					Rail r = new Rail(stateName, delimiter);
					r.read(in, stateName);
					rail.put(r.getName(), r);
					
				}
				else if(classtype.equalsIgnoreCase("CustomPoint"))
				{
					CustomPoint r = new CustomPoint(stateName, delimiter);
					r.read(in, stateName);
					customPoint.put(r.getName(), r);
				}
				else
				{
					// do nothing 
				}
			
				stateName = in.readLine().trim();
			}
			
		}
	}
	
	
	public void checkLanLog(State r)
	{
		
		if (r.getMinLon() < minLon){
			
            minLon = r.getMinLon();
		}
        if (r.getMaxLon() > maxLon){
            maxLon = r.getMaxLon();
            
        }
        if (r.getMinLat() < minLat){
            minLat = r.getMinLat();
           
        }
        if (r.getMaxLat() > maxLat){
            maxLat = r.getMaxLat();
        }
        
		
		bounds = new Rectangle2D.Double(minLon, minLat, maxLon - minLon, maxLat - minLat);
		
	}
}

	